package com.robertux.leaderboard.servlets;

import java.io.IOException;
import java.math.BigDecimal;
import java.text.DateFormat;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.Locale;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.log4j.Logger;

import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.robertux.leaderboard.beans.JsonResponse;
import com.robertux.leaderboard.beans.RolUsuario;
import com.robertux.leaderboard.beans.entities.ProyeccionesFarmacia;
import com.robertux.leaderboard.beans.entities.SucursalSupervision;
import com.robertux.leaderboard.beans.entities.Usuario;
import com.robertux.leaderboard.beans.entities.external.Sucursal;
import com.robertux.leaderboard.util.MethodExecutionSecurity;
import com.robertux.leaderboard.util.MethodParamsChecker;
import com.robertux.leaderboard.util.gestoresModelos.GestorEntidadesFarmacia;
import com.robertux.leaderboard.util.gestoresModelos.GestorProyeccionesFarmacias;
import com.robertux.leaderboard.util.gestoresModelos.GestorUsuarios;

@WebServlet("/ProyeccionesServlet/*")
public class ProyeccionesServlet extends BaseServlet {
	private static final long serialVersionUID = 1L;
	private GestorProyeccionesFarmacias gProyecciones = new GestorProyeccionesFarmacias();
	private GestorEntidadesFarmacia gEntsFrm = new GestorEntidadesFarmacia();
	private GestorUsuarios gUsrs = new GestorUsuarios();
	private Logger logger = Logger.getLogger(this.getClass());
	private DateFormat dFmt = new SimpleDateFormat("MMMMM yyyy", new Locale("es"));
	private NumberFormat nFmt = new DecimalFormat("#.00");
    
	@MethodExecutionSecurity(requireAuthentication=true, roleLevelAllowed=RolUsuario.VENDEDOR)
    public JsonResponse getMesesProyecciones(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException {
    	JsonResponse jsonResp;
    	JsonArray arr = new JsonArray();
    	
    	Calendar cal = Calendar.getInstance();
    	cal.add(Calendar.MONTH, -3);
    	for(int i=0; i<4; i++) {
    		JsonObject obj = new JsonObject();
    		obj.addProperty("year", cal.get(Calendar.YEAR));
    		obj.addProperty("month", cal.get(Calendar.MONTH)+1);
    		obj.addProperty("label", dFmt.format(cal.getTime()));
    		arr.add(obj);
    		cal.add(Calendar.MONTH, +1);
    	}
    	
    	jsonResp = new JsonResponse(0, "OK", "meses", arr);
    	return jsonResp;
    }
	
	@MethodExecutionSecurity(requireAuthentication=true, roleLevelAllowed=RolUsuario.ADMIN)
	public JsonResponse getProyeccionesAdministrar(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException {
		JsonResponse jsonResp = new JsonResponse();
		JsonArray arr = new JsonArray();
		Calendar calHoy = new GregorianCalendar();
		Calendar calFecha = new GregorianCalendar();
		
		int anio = 0, mes = 0;
		
		if(req.getParameter("anio") != null && req.getParameter("anio").trim().length() > 0
				&& req.getParameter("mes") != null && req.getParameter("mes").trim().length() > 0) {
			try {
				anio = Integer.parseInt(req.getParameter("anio"));
				mes = Integer.parseInt(req.getParameter("mes"));
			} catch(Exception ex) {
				this.logger.error("Error tratando de parsear el año y mes recibidos: " + ex.getMessage(), ex);
			}
		}
		calFecha.set(Calendar.MONTH, mes-1);
		calFecha.set(Calendar.YEAR, anio);
		
		
		List<Sucursal> sucursales = this.gEntsFrm.getSucursales();
		for(Sucursal suc: sucursales) {
			JsonObject obj = new JsonObject();
			ProyeccionesFarmacia proyeccion = this.gProyecciones.getProyeccion(suc.getId(), anio, mes);
			
			obj.addProperty("branchId", suc.getId());
			obj.addProperty("branchName", suc.getNombre());
			obj.addProperty("mes", dFmt.format(calFecha.getTime()));
			obj.addProperty("meta", proyeccion != null? proyeccion.getMeta(): 0);
			obj.addProperty("diasApertura", proyeccion != null? proyeccion.getDiasApertura(): 0);
			obj.addProperty("editable", (calHoy.get(Calendar.YEAR) == calFecha.get(Calendar.YEAR) 
					&& calHoy.get(Calendar.MONTH) == calFecha.get(Calendar.MONTH)));
			
			arr.add(obj);
		}
		
		jsonResp.add("proyeccionesAdministrar", arr);
		return jsonResp;
	}
	
	@MethodExecutionSecurity(requireAuthentication=true, roleLevelAllowed=RolUsuario.ADMIN)
	@MethodParamsChecker(requiredParams={"branchId-meta-dias"})
	public JsonResponse updateMetasProyeccionFarmacia(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException {
		JsonResponse jsonResp = new JsonResponse();
		Calendar calHoy = new GregorianCalendar();
		
		String[] branchesMetas = req.getParameter("branchId-meta-dias").split(",");
		
		for(String branchMeta: branchesMetas) {
			String[] datosBranchMeta = branchMeta.split("-"); 
			String branchId = datosBranchMeta[0];
			String meta = datosBranchMeta[1];
			String dias = datosBranchMeta[2];
			
			ProyeccionesFarmacia proyeccion = this.gProyecciones.getProyeccion(Integer.parseInt(branchId), calHoy.get(Calendar.YEAR), calHoy.get(Calendar.MONTH)+1);
			if(proyeccion == null) {
				proyeccion = new ProyeccionesFarmacia(Integer.parseInt(branchId), calHoy.getTime(), new BigDecimal(meta), BigDecimal.ZERO, Integer.parseInt(dias));
			} else {
				proyeccion.setMeta(new BigDecimal(meta));
				proyeccion.setDiasApertura(Integer.parseInt(dias));
			}
			
			if(this.gProyecciones.actualizarProyeccion(proyeccion)) {
				jsonResp.setReason("Metas globales actualizadas satisfactoriamente");
			} else {
				jsonResp = JsonResponse.fromError(1057, req.getParameter("branchId"));
			}
		}
		
		return jsonResp;
	}
	
	@MethodExecutionSecurity(requireAuthentication=true, roleLevelAllowed=RolUsuario.JEFE)
	public JsonResponse getProyeccionesSucursales(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException {
		JsonResponse jsonResp = new JsonResponse();
		JsonArray arr = new JsonArray();
		Calendar calHoy = new GregorianCalendar();
		List<Sucursal> sucursales = new ArrayList<Sucursal>();
		
		Usuario usr = null;
		int anio = 0, mes = 0;
		
		if(req.getParameter("userId") != null && req.getParameter("userId").trim().length() > 0) {
			usr = this.gUsrs.getUsuario(Integer.parseInt(req.getParameter("userId")));
		} else {
			usr = this.getCurrentUsuario(req);
		}
		
		if(req.getParameter("anio") != null && req.getParameter("anio").trim().length() > 0
				&& req.getParameter("mes") != null && req.getParameter("mes").trim().length() > 0) {
			try {
				anio = Integer.parseInt(req.getParameter("anio"));
				mes = Integer.parseInt(req.getParameter("mes"));
			} catch(Exception ex) {
				this.logger.error("Error tratando de parsear el año y mes recibidos: " + ex.getMessage(), ex);
			}
		}
		
		if(usr != null) {
			switch(RolUsuario.fromNivel(usr.getRol().getNivel())) {
			case JEFE:
				sucursales.add(this.gEntsFrm.getSucursal(usr.getSucursalId()));
				break;
			case SUPERVISOR:
				for(SucursalSupervision sucSup: usr.getSucursalesSupervision()) {
					Sucursal sucSupFarmacia = this.gEntsFrm.getSucursal(sucSup.getSucursalId());
					sucursales.add(sucSupFarmacia);
				}
				break;
			case GERENTE:
			case ADMIN:
				sucursales.addAll(this.gEntsFrm.getSucursales());
			default:
				break;
			}
			
			for(Sucursal suc: sucursales) {
				ProyeccionesFarmacia proyeccion = this.gProyecciones.getProyeccion(suc.getId(), anio, mes);
				double proyeccionVentas = proyeccion != null? (proyeccion.getVentas().doubleValue() / (calHoy.get(Calendar.DAY_OF_MONTH)-1) * ((double)proyeccion.getDiasApertura())): 0;
				
				JsonObject obj = new JsonObject();
				obj.addProperty("id", proyeccion != null? proyeccion.getId(): 0);
				obj.addProperty("mes", this.dFmt.format(calHoy.getTime()));
				obj.addProperty("branchId", suc.getId());
				obj.addProperty("branchName", suc.getNombre());
				obj.addProperty("meta", proyeccion != null? proyeccion.getMeta(): 0);
				obj.addProperty("ventaAcumulada", proyeccion != null? this.nFmt.format(proyeccion.getVentas()): "0");
				obj.addProperty("proyeccionVentas", Math.round((proyeccionVentas)));
				obj.addProperty("tendencia", proyeccion != null && proyeccion.getMeta().doubleValue() > 0? this.nFmt.format((100 * proyeccionVentas / proyeccion.getMeta().doubleValue())): "0");
				arr.add(obj);
			}
		}
		
		jsonResp.add("proyecciones", arr);
		return jsonResp;
	}
	
	@MethodExecutionSecurity(requireAuthentication=true, roleLevelAllowed=RolUsuario.JEFE)
	@MethodParamsChecker(requiredParams={"idProyeccion"})
	public JsonResponse getDetalleProyeccionSucursal(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException {
		JsonResponse jsonResp = new JsonResponse();
		ProyeccionesFarmacia proyeccion, proyeccionActual;
		Calendar cal = new GregorianCalendar();
		JsonObject objProyecciones = new JsonObject();
		
		proyeccionActual = this.gProyecciones.getProyeccion(Integer.parseInt(req.getParameter("idProyeccion")));		//Mes actual
		double valorProyeccionActual = proyeccionActual != null? (proyeccionActual.getVentas().doubleValue() / cal.get(Calendar.DAY_OF_MONTH) * cal.getActualMaximum(Calendar.DAY_OF_MONTH)): 0;
		objProyecciones.add("month", this.proyeccionToJson(proyeccionActual != null? proyeccionActual.getFarmaciaId(): 0, cal));
		
		cal.add(Calendar.MONTH, -3);																					//Hace tres meses
		objProyecciones.add("month3", this.proyeccionToJson(proyeccionActual != null? proyeccionActual.getFarmaciaId(): 0, cal));
		
		cal.add(Calendar.MONTH, +1);																					//Hace dos meses
		objProyecciones.add("month2", this.proyeccionToJson(proyeccionActual != null? proyeccionActual.getFarmaciaId(): 0, cal));
		
		cal.add(Calendar.MONTH, +1);																					//Hace un mes
		objProyecciones.add("month1", this.proyeccionToJson(proyeccionActual != null? proyeccionActual.getFarmaciaId(): 0, cal));
		
		cal.add(Calendar.MONTH, +1);																					//Hace un anio
		cal.add(Calendar.YEAR, -1);
		objProyecciones.add("year2", this.proyeccionToJson(proyeccionActual != null? proyeccionActual.getFarmaciaId(): 0, cal));
		
		Calendar calHoy = new GregorianCalendar();																		//Hace un anio vs mes actual
		proyeccion = this.gProyecciones.getProyeccion(proyeccionActual != null? proyeccionActual.getFarmaciaId(): 0, cal.get(Calendar.YEAR), cal.get(Calendar.MONTH)+1);
		objProyecciones.add("year1", this.proyeccionComparadaToJson(proyeccion, valorProyeccionActual, cal, calHoy));
		
		Sucursal suc = this.gEntsFrm.getSucursal(proyeccionActual.getFarmaciaId());
		objProyecciones.addProperty("branchName", suc != null? suc.getNombre(): "(ninguna)");
		jsonResp.add("detalleProyeccion", objProyecciones);
		
		return jsonResp;
	}
	
	@MethodExecutionSecurity(requireAuthentication=true, roleLevelAllowed=RolUsuario.GERENTE)
	public JsonResponse getProyeccionesGenerales(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException {
		JsonResponse jsonResp = new JsonResponse();
		ProyeccionesFarmacia proyeccionActual;
		Calendar cal = new GregorianCalendar();
		JsonObject objProyecciones = new JsonObject();
		
		proyeccionActual = this.getTotalesProyeccion(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH)+1);				//Mes actual
		double valorProyeccionActual = proyeccionActual != null? (proyeccionActual.getVentas().doubleValue() / cal.get(Calendar.DAY_OF_MONTH) * cal.getActualMaximum(Calendar.DAY_OF_MONTH)): 0;
		objProyecciones.add("month", this.proyeccionToJson(cal));
		
		cal.add(Calendar.MONTH, -3);																					//Hace tres meses
		objProyecciones.add("month3", this.proyeccionToJson(cal));
		
		cal.add(Calendar.MONTH, +1);																					//Hace dos meses
		objProyecciones.add("month2", this.proyeccionToJson(cal));
		
		cal.add(Calendar.MONTH, +1);																					//Hace un mes
		objProyecciones.add("month1", this.proyeccionToJson(cal));
		
		cal.add(Calendar.MONTH, +1);																					//Hace un anio
		cal.add(Calendar.YEAR, -1);
		objProyecciones.add("year2", this.proyeccionToJson(cal));
		
		Calendar calHoy = new GregorianCalendar();																		//Hace un anio vs mes actual
		objProyecciones.add("year1", this.proyeccionComparadaToJson(proyeccionActual, valorProyeccionActual, cal, calHoy));
		
		jsonResp.add("proyeccionesGenerales", objProyecciones);
		return jsonResp;
	}
	
	@MethodExecutionSecurity(requireAuthentication=true, roleLevelAllowed=RolUsuario.GERENTE)
	public JsonResponse getDetalleProyeccionesFarmacias(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException {
		JsonResponse jsonResp = new JsonResponse();
		JsonArray arr = new JsonArray();
		
		List<Sucursal> sucursales = this.gEntsFrm.getSucursales();
		for(Sucursal suc: sucursales) {
			ProyeccionesFarmacia proyeccion, proyeccionActual;
			Calendar cal = new GregorianCalendar();
			JsonObject objProyecciones = new JsonObject();
			
			proyeccionActual = this.gProyecciones.getProyeccion(suc.getId(), cal.get(Calendar.YEAR), cal.get(Calendar.MONTH)+1);	//Mes actual
			double valorProyeccionActual = proyeccionActual != null? (proyeccionActual.getVentas().doubleValue() / cal.get(Calendar.DAY_OF_MONTH) * cal.getActualMaximum(Calendar.DAY_OF_MONTH)): 0;
			objProyecciones.add("month", this.proyeccionToJson(proyeccionActual != null? proyeccionActual.getFarmaciaId(): 0, cal));
			
			cal.add(Calendar.MONTH, -3);																				//Hace tres meses
			objProyecciones.add("month3", this.proyeccionToJson(proyeccionActual != null? proyeccionActual.getFarmaciaId(): 0, cal));
			
			cal.add(Calendar.MONTH, +1);																				//Hace dos meses
			objProyecciones.add("month2", this.proyeccionToJson(proyeccionActual != null? proyeccionActual.getFarmaciaId(): 0, cal));
			
			cal.add(Calendar.MONTH, +1);																				//Hace un mes
			objProyecciones.add("month1", this.proyeccionToJson(proyeccionActual != null? proyeccionActual.getFarmaciaId(): 0, cal));
			
			cal.add(Calendar.MONTH, +1);																				//Hace un anio
			cal.add(Calendar.YEAR, -1);
			objProyecciones.add("year2", this.proyeccionToJson(proyeccionActual != null? proyeccionActual.getFarmaciaId(): 0, cal));
			
			Calendar calHoy = new GregorianCalendar();																	//Hace un anio vs mes actual
			proyeccion = this.gProyecciones.getProyeccion(proyeccionActual != null? proyeccionActual.getFarmaciaId(): 0, cal.get(Calendar.YEAR), cal.get(Calendar.MONTH)+1);
			objProyecciones.add("year1", this.proyeccionComparadaToJson(proyeccion, valorProyeccionActual, cal, calHoy));
			
			objProyecciones.addProperty("branchName", suc != null? suc.getNombre(): "(ninguna)");
			arr.add(objProyecciones);
		}
		
		jsonResp.add("detalleProyeccionesFarmacias", arr);
		
		
		return jsonResp;
	}
	
	protected JsonObject proyeccionToJson(Calendar cal) {
		JsonObject obj = new JsonObject();
		ProyeccionesFarmacia proyeccion = this.getTotalesProyeccion(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH)+1);
		obj.addProperty("label", this.dFmt.format(cal.getTime()));
		obj.addProperty("sales", proyeccion != null? this.nFmt.format(proyeccion.getVentas().doubleValue()): "0");
		obj.addProperty("value", proyeccion != null && proyeccion.getMeta().doubleValue() > 0? this.nFmt.format((100 * proyeccion.getVentas().doubleValue() / proyeccion.getMeta().doubleValue())): "0");
		
		return obj;
	}
	
	protected JsonObject proyeccionToJson(int branchId, Calendar cal) {
		JsonObject obj = new JsonObject();
		ProyeccionesFarmacia proyeccion = this.gProyecciones.getProyeccion(branchId, cal.get(Calendar.YEAR), cal.get(Calendar.MONTH)+1);
		obj.addProperty("label", this.dFmt.format(cal.getTime()));
		obj.addProperty("sales", proyeccion != null? this.nFmt.format(proyeccion.getVentas().doubleValue()): "0");
		obj.addProperty("value", proyeccion != null && proyeccion.getMeta().doubleValue() > 0? this.nFmt.format((100 * proyeccion.getVentas().doubleValue() / proyeccion.getMeta().doubleValue())): "0");
		
		return obj;
	}
	
	protected JsonObject proyeccionComparadaToJson(ProyeccionesFarmacia proyeccion, double valorProyeccionActual, Calendar cal, Calendar calHoy) {
		JsonObject obj = new JsonObject();
		obj.addProperty("label", this.dFmt.format(cal.getTime()) + " VS " + this.dFmt.format(calHoy.getTime()));
		obj.addProperty("sales", 0);
		obj.addProperty("value", proyeccion != null && proyeccion.getVentas().doubleValue() > 0? this.nFmt.format((100 * valorProyeccionActual / proyeccion.getVentas().doubleValue())): "0");
		return obj;
	}
	
	protected ProyeccionesFarmacia getTotalesProyeccion(int anio, int mes) {
		ProyeccionesFarmacia totalProyeccion = new ProyeccionesFarmacia(0, new Date(), new BigDecimal(0), new BigDecimal(0), 0);
		for(ProyeccionesFarmacia proyeccion: this.gProyecciones.getProyecciones(anio, mes)) {
			totalProyeccion.getMeta().add(proyeccion.getMeta());
			totalProyeccion.getVentas().add(proyeccion.getVentas());
		}
		
		if(totalProyeccion.getMeta().equals(BigDecimal.ZERO)) {
			totalProyeccion.setMeta(BigDecimal.ONE);
		}
		return totalProyeccion;
	}
}
